import { G, SVG, Svg } from '@svgdotjs/svg.js'
import { sizeConfig } from '../config/sizeConfig'
import { IPoint } from '../types'
import { getCenterPoint } from '../utils'

const busWidth = sizeConfig.bus.width

// 画一个箭头，指向指定的点
function drawArrow(point: IPoint, from: 'left' | 'right' = 'left'): string {
    const width = busWidth
    const height = 12

    const { x, y } = point

    if (from === 'right') {
        return `L ${x} ${y + height}
    l ${width / 2} 0
    l -${width} -${height}
    l -${width} ${height}
    l ${width / 2} 0`
    }

    return `
    L ${x} ${y + height}
    l -${width / 2} 0
    l ${width} -${height}
    l ${width} ${height}
    l -${width / 2} 0
    `
}

// 获取RamToMultiplexer21每个拐点的坐标
export function getRamToMultiplexer21Points() {
    // ram上方的点
    const point1 = getCenterPoint(sizeConfig.ram, 'top')
    point1.x -= busWidth
    point1.y -= 34 + 5
    // ram到最右边的点
    const point2 = {
        x: point1.x + 460,
        y: point1.y,
    }
    // 2-1选择器下面的点
    const point3 = getCenterPoint(sizeConfig.multiplexer21, 'bottom')
    point3.x += sizeConfig.multiplexer21.width / 4 - 4.5 // 加上宽度的一半，减去箭头的宽度的一半
    // 2-1下方的拐点
    const point4 = {
        x: point3.x,
        y: point3.y + busWidth * 4,
    }

    // ir下方
    const point5 = getCenterPoint(sizeConfig.ir, 'bottom')
    point5.x -= 4.5

    // 输入左边的点
    const point6 = getCenterPoint(sizeConfig.input, 'left')
    point6.y -= 4.5

    // 输出左边的点
    const point7 = getCenterPoint(sizeConfig.output, 'left')
    point7.y -= 4.5

    return {
        point1,
        point2,
        point3,
        point4,
        point5,
        point6,
        point7,
    }
}

// 从ram到2-1选择器，1号总线
function initRamToMultiplexer21(draw: G) {
    const { point1, point2, point3, point4, point5, point6, point7 } =
        getRamToMultiplexer21Points()
    draw.path(
        `
    M ${point1.x} ${point1.y}
    L ${point5.x} ${point1.y} 
    ${drawArrow(point5)}
    L ${point5.x + busWidth} ${point1.y} 
    L ${point2.x} ${point2.y}
    L ${point2.x} ${point6.y}
    L ${point6.x} ${point6.y}
    l 0 ${busWidth}
    L ${point2.x} ${point6.y + busWidth}
    L ${point2.x} ${point7.y}
    L ${point7.x} ${point7.y}
    l 0 ${busWidth}
    L ${point2.x} ${point7.y + busWidth}
    L ${point2.x} ${point4.y}
    L ${point3.x} ${point4.y}
    l 0 -${busWidth}
    ${drawArrow(point3)}
    L ${point3.x + busWidth} ${point4.y - busWidth}
    L ${point2.x - busWidth} ${point4.y - busWidth}
    L ${point2.x - busWidth} ${point2.y + busWidth}
    L ${point1.x} ${point1.y + busWidth}
    L ${point1.x} ${point1.y}
`
    )
        .stroke({
            color: '#000',
            linejoin: 'arcs',
        })
        .fill('#fff')
}

// 获取PcToMultiplexer每个拐点的坐标
export function getPcToMultiplexerPoints() {
    // pc上方的点
    const point1 = getCenterPoint(sizeConfig.pc, 'top')
    point1.x += 4 // 加上箭头的宽度的一半

    // 中间的拐角处
    const point2 = {
        x: point1.x + 150,
        y: point1.y - 25,
    }

    // 2-1选择器下面的点
    const point3 = getCenterPoint(sizeConfig.multiplexer21, 'bottom')
    point3.x -= sizeConfig.multiplexer21.width / 4 - 4.5 // 加上宽度的一半，减去箭头的宽度的一半

    // 2-1下面的拐点
    const point4 = {
        x: point3.x,
        y: point3.y + busWidth * 4,
    }

    // 3-1选择器下面的点
    const point5 = getCenterPoint(sizeConfig.multiplexer31, 'bottom')
    point5.x -= (sizeConfig.multiplexer31.width / 6) * 2 - 4.5

    return {
        point1,
        point2,
        point3,
        point4,
        point5,
    }
}

// 从pc到两个，2号总线
function initPcToMultiplexer(draw: G) {
    const { point1, point2, point3, point4, point5 } =
        getPcToMultiplexerPoints()

    draw.path(
        `
    M ${point1.x} ${point1.y}
    L ${point1.x} ${point2.y}
    L ${point2.x} ${point2.y}
    L ${point2.x} ${point4.y}
    L ${point4.x} ${point4.y}
    l 0 -${busWidth}
    ${drawArrow(point3, 'right')}
    L ${point4.x - busWidth} ${point4.y - busWidth}
    L ${point2.x + busWidth} ${point4.y - busWidth}
    L ${point2.x + busWidth} ${point2.y - busWidth}
    L ${point1.x} ${point2.y - busWidth}
    l 0 -73
    ${drawArrow(point5, 'right')}
    L ${point1.x - busWidth} ${point1.y}
    `
    )
        .stroke({
            color: '#000',
            linejoin: 'arcs',
        })
        .fill('#fff')
}

// 获取RegDToMultiplexer31AndAuB的拐点坐标
export function getRegDToMultiplexer31AndAuBPoints() {
    // 通用寄存器的D口
    const point1 = getCenterPoint(sizeConfig.reg, 'top')
    point1.x += sizeConfig.reg.width / 4 - 4.5 // 减去箭头的宽度的一半
    // 3-1选择器的下面的2口
    const point2 = getCenterPoint(sizeConfig.multiplexer31, 'bottom')
    point2.x += (sizeConfig.multiplexer31.width / 6) * 2 - 4.5 // 加上宽度的一半，减去箭头的宽度的一半

    // AU下面的B口
    const point3 = getCenterPoint(sizeConfig.au, 'bottom')
    point3.x += sizeConfig.au.width / 4 - 4.5

    // 3-1选择器下面的拐点
    const point4 = {
        x: point2.x,
        y: point1.y - busWidth * 3,
    }

    return {
        point1,
        point2,
        point3,
        point4,
    }
}

// 从通用寄存器的D口到3-1选择器和AU的B口，4号总线
function initRegDToMultiplexer31AndAuB(draw: G) {
    const { point1, point2, point3, point4 } =
        getRegDToMultiplexer31AndAuBPoints()

    draw.path(
        `
    M ${point1.x} ${point1.y}
    L ${point1.x} ${point4.y}
    L ${point4.x} ${point4.y}
    l 0 -${busWidth}
    ${drawArrow(point2)}
    L ${point4.x + busWidth} ${point4.y - busWidth}
    L ${point1.x} ${point4.y - busWidth}
    ${drawArrow(point3)}
    L ${point1.x + busWidth} ${point1.y} 
    `
    )
        .stroke({
            color: '#000',
            linejoin: 'arcs',
        })
        .fill('#fff')
}

// 获取RegSToMultiplexer31AndAuA的拐点坐标
export function getRegSToMultiplexer31AndAuAPoints() {
    // 通用寄存器的S口
    const point1 = getCenterPoint(sizeConfig.reg, 'top')
    point1.x -= sizeConfig.reg.width / 4 + 4.5 // 加上箭头的宽度的一半

    // 3-1选择器的下面的1口
    const point2 = getCenterPoint(sizeConfig.multiplexer31, 'bottom')
    point2.x += -4 // 减去箭头的宽度的一半

    // AU下面的A口
    const point3 = getCenterPoint(sizeConfig.au, 'bottom')
    point3.x -= sizeConfig.au.width / 4 + 3.5

    // 3-1选择器下方的拐点
    const point4 = {
        x: point2.x,
        y: point1.y - busWidth,
    }

    // 分流到PC处的拐点
    const point5 = {
        x: sizeConfig.pc.x + sizeConfig.pc.width + 50,
        y: point3.y,
    }

    // PC下方的点
    const point6 = getCenterPoint(sizeConfig.pc, 'bottom')
    point6.x -= 4.5

    // PC下方的拐点
    const point7 = {
        x: point6.x,
        y: point6.y + busWidth * 4,
    }

    return {
        point1,
        point2,
        point3,
        point4,
        point5,
        point6,
        point7,
    }
}

// 从通用寄存器的S口到3-1选择器和AU的A口、PC的入口，3号总线
function initRegSToMultiplexer31AndAuA(draw: G) {
    const { point1, point2, point3, point4, point5, point6, point7 } =
        getRegSToMultiplexer31AndAuAPoints()

    draw.path(
        `
    M ${point1.x} ${point1.y}
    L ${point1.x} ${point4.y}
    L ${point5.x} ${point4.y}
    L ${point5.x} ${point7.y}
    L ${point6.x} ${point7.y}
    l 0 -${busWidth}
    ${drawArrow(point6)}
    L ${point6.x + busWidth} ${point7.y - busWidth}
    L ${point5.x - busWidth} ${point7.y - busWidth}
    L ${point5.x - busWidth} ${point4.y}
    L ${point4.x} ${point4.y}
    L ${point4.x} ${point4.y - busWidth * 3}
    ${drawArrow(point2)}
    L ${point4.x + busWidth} ${point4.y - busWidth}
    L ${point1.x} ${point4.y - busWidth}
    ${drawArrow(point3)}
    L ${point1.x + busWidth} ${point1.y}
    `
    )
        .stroke({
            color: '#000',
            linejoin: 'arcs',
        })
        .fill('#fff')
}

/**
 * 用一个箭头连接两个点
 * @param point1 第一个点
 * @param point2 第二个点
 * @param draw 给定一个svg对象
 */
function linkTwoPointByArrow(point1: IPoint, point2: IPoint, draw: Svg) {
    const group = draw.group()
    point1.x -= 4.5
    point2.x -= 4.5

    group
        .path(
            `
        M ${point1.x} ${point1.y}
        ${drawArrow(point2)}
        L ${point1.x + busWidth} ${point1.y}
        `
        )
        .stroke({
            color: '#000',
            linejoin: 'arcs',
        })
        .fill('#fff')
}

export function initBusShape() {
    const draw = SVG()
        .addTo('.vspm-container .bus-container')
        .size(sizeConfig.container.width, sizeConfig.container.height)

    const busRamToMultiplexer21 = draw.group()
    initRamToMultiplexer21(busRamToMultiplexer21)
    const busPcToMultiplexer = draw.group()
    initPcToMultiplexer(busPcToMultiplexer)
    const busRegDToMultiplexer31AndAuB = draw.group()
    initRegDToMultiplexer31AndAuB(busRegDToMultiplexer31AndAuB)
    const busRegSToMultiplexer31AndAuA = draw.group()
    initRegSToMultiplexer31AndAuA(busRegSToMultiplexer31AndAuA)

    // 连接一些模块
    linkTwoPointByArrow(
        getCenterPoint(sizeConfig.ir, 'top'),
        getCenterPoint(sizeConfig.decoder, 'bottom'),
        draw
    )
    linkTwoPointByArrow(
        getCenterPoint(sizeConfig.decoder, 'top'),
        getCenterPoint(sizeConfig.conSignal, 'bottom'),
        draw
    )
    linkTwoPointByArrow(
        getCenterPoint(sizeConfig.multiplexer31, 'top'),
        getCenterPoint(sizeConfig.ram, 'bottom'),
        draw
    )
    linkTwoPointByArrow(
        getCenterPoint(sizeConfig.multiplexer21, 'top'),
        getCenterPoint(sizeConfig.reg, 'bottom'),
        draw
    )
    const ramTop = getCenterPoint(sizeConfig.ram, 'top')
    linkTwoPointByArrow(ramTop, { ...ramTop, y: ramTop.y - busWidth * 4 }, draw)
    const auTop = getCenterPoint(sizeConfig.au, 'top')
    linkTwoPointByArrow(auTop, { ...auTop, y: auTop.y - busWidth * 4 }, draw)
}
